نظام I/O الدخل والخرج مصمم لعمل مع مختلف الاجهزة فهو يتفاعل مع المستخدم للوصول الى الاجهزة وهذا التفاعل يسمى بالسيل stream ، فمصطلح السيل هو مسار البيانات الماره في البرنامج وتدفق البيانات بشكل سيل مستمر ومتسلسل بين البرنامج والمصدر ( وقد يكون المصدر ملف فيزيائي على القرص او جهاز كاشاشة او لوحة المفاتيح او قد يكون اتصال شبكة )من خلال مقابض الملفات Filehandles ، سوف يستلم برنامج البيرل البيانات المدخلة من جهاز الدخل (لوحة المفاتيح) الى مقبض الملف STDIN الذي يقوم بفك ضغط البيانات وتسلمها الى البرنامج ، وحينما البرنامج يرسل بيانات لاخراجها على جهاز الخرج (الشاشة) مقبض الملف STDOUT يستلم تلك البيانات ثم يقوم بضغطها ويرسلها الى جهاز الخرج .
مقابض الملف هي للإتصال بين البرنامج والعتاد وفي قلب كل مقبض ملف file descriptor ملف وصف لدية قيمة صحيحة التي تمثل البيانات stream المتدفقة الخامة والتي يستعملها نظام التشغيل، وكذلك نستطيع الوصول المباشر لملف الوصف فهو بشكل خاص ذات أعداد صحيحة غير سالبة التي تستعملها النواة لتميز الملفات للوصل اليها من قبل عملية خاصة ، فحين فتح ملف موجود او انشاء ملف جديد فأن النواة تعيد قيمة ملف الوصف التي يمكن استخدامها عند الرغبة بالقراءة او الكتابة الى الملف ، ترتبط شل اليونكس بالملف الوصف بالقيمة 0 مع الدخل القياسئ ، وبالقيمة 1 مع الخرج القياسي ، وبالقيمة 2 مع الخطأ القياسي ، وبشكل مشابة لمبرمجين السيء للخرج والدخل الـ buffering او التخزين المؤقت للمقبض المنجزة بواسطة streams التي هي ايضاُ مستندة على الـ file descriptors ، في الحقيقة فأن مقابض الملفات للبيرل يلف الـ file descriptors في نفس الطريقة تقريباً ، وعلى اي حال فان البيرل لاتستعمل streams مباشرتاً وبدلاً من ذلك فهي بضمن معظم الوضائف ضمن المفسر نفسة ، هذا يعني بان معالجة البيرل للمقابض الملفات شكل مشابة للسي لكن في البيرل 5.8 فقواعد البيرل تغيرت بواسطة PerlIO
برنامج البيرل يبداء بثلاثه مقابض للملف مفتوحة كافتراضي وهم :
STDOUT أو standard output أو الخرج القياسي هو مقبض الملف الافتراضي للخرج ،فالبيانات ترسل الى مقبض الملف وتظهر للمستخدم على الشاشة
STDIN أو standard input أو الدخل القياسي هو مقبض الملف الافتراضي للدخل ،البيانات تقراء من مقبض الملف وتأخذ من المستخدم لكي تنجز
STDERR أو Standard error أو الخطأ القياسي لرسائل الخطاء
المثال التالي لإدخال المستخدم من خلال المقبض STDIN واخراج البيانات بواسطة المقبض STDOUT
#!/usr/bin/perl $input = <STDIN>; chomp($input); print STDOUT "If I heard you correctly, you said: $input\n";
وبنفس الطريقة
#!/usr/bin/perl chomp($input = <>); print "If I heard you correctly, you said: $input\n";
بإمكان استخدام التوجيه redirect باستعمال رموز metacharacters "<" , ">". بأدخال الامر في سطر الاوامر حيث ملف data.txt هو الدخل القياسي و crunched.txt هو الخرج القياسي
Perl muncher.pl <data.txt >crunched.txt
بنفس الطريقة لقراءة ملف اوالكتابة عليه ولكن اذا اردت فتح الملف للقراءة يتم فتح الملف للدخل وعليك استعمال الدوال التالية :
الدالة open لفتح الملف الى مقبض الملف
الدالة read لقراءة البيانات من مقبض الملف
الدالة close لإغلاق مقبض الملف
واذا اردت فتح الملف للكتابة يتم فتح الملف للخرج ويجب عليك استعمال الدوال التالية :
الدالة open لفتح الملف الى مقبض الملف
الدالة print لكتابة البيانات الى مقبض الملف
الدالة close لإغلاق مقبض الملف
برنامج بسيط يقوم بفتح الملف والقراءة منه ثم غلقه وثم طباعة محتواه
#!/usr/bin/perl $file = '/etc/passwd'; # Name the file open(INFO, $file); # Open the file @lines =<INFO> ; # Read it into an array close(INFO); # Close the file print @lines; # Print the array
الدالة open()
الصيغة:
$success = open(FILEHANDLE,$path) $success = open(FILEHANDLE,$mode,$path)
تقوم هذة الدالة بفتح الملف ، من الممكن ان تحمل open اثنين او ثلاثة بارامتر ، فأول بارامتر مقبض الملف الذي يمنح لبرنامج البيرل لكي يشير إلى الملف لاحقاً ، وثاني بارامتر أسم الملف يوضع في علامات الاقتباس ، نستطيع وضع النمط mode اما باثنين بارامتر او ثلاثه بارامتر فعلى سبيل المثال
open(FILEHANDLE,'>',$path); open(FILEHANDLE,'>$path');
يختلف غرض فتح الملف بإختلاف النمط كالقراءة والكتابة وهنا بعض الانماط المستخدمة في البيرل
| النمط | الوصف |
|---|---|
| < | فتح الملف للقراءة ،وهو الافتراضي فبعدم تضمين اي نمط يضمن تلقائياً لفتح الملف ، ويفشل بالفتح في حال الملف غير موجود |
| > | حذف وازالة ما داخل الملف وفتح الملف للكتابة ،اذا الملف غير موجود سيقوم بنشاءه |
| >> | فتح الملف ووضع المؤشر الى نهاية الملف append ، لايعمل له ازالة ،اذا الملف غير موجود يقوم بنشاءه |
| +> | ازالة مايحتوية الملف ،وفتح الملف للقراءة والكتابة ،اذا الملف غير موجود يقوم بنشاءه |
| <+ | فتح الملف للقراءة والكتابة ، لايعمل له ازالة وستبداء القراءة والكتابة من بداية الملف وأن الملف غير موجود يفشل بالفتح |
open(FILEHANDLE, $path); # Open for input open(FILEHANDLE, "<$path"); # Also open for input open(FILEHANDLE, ">$path"); # Open for output open(FILEHANDLE, ">>$path"); # Open for appending
من الممكن إزالة اقواس الدالة بدون تاثير
open INFO,"log.txt" or die "Couldn't open log.txt: $!"
إذا كان اسم الملف معطى في علامة اقتباس ثم يأخذ علامة التمدد في الشل لان ينجح الأمر لذلك التعبير '~/notes/todolist' لان يترجم ،وإذا أردت إجبار الشل بالعمل مع علامة التمدد يتم وضعه <~/notes/todolist>
انماط اخر :
يمكن عمل نسخ لمقابض الملف duplicate بواسطة إستخدام النمط >& or <& ، فالـ >& يقوم بعمل نسخ لمقبض الملف للكتابة ، و <& نسخ مقبض الملف للقراءة
open (OUTCOPY,">&STDOUT"); open (INCOPY,"<&STDOUT");
من الممكن استبدال اسم مقبض الملف بالقيم العددية لملف الوصف المحدد 0 ، 1 ،2 لكل من STDIN,STDOUT, STDERR على التوالي و في حال الملف موجود من خلال الدالة fileno
open(OUTCOPY, "> &1"); # duplicate fileno 1 (STDOUT) open(INCOPY, "< &0"); # duplicate fileno 0 (STDIN)
في المثال السابق انشاء مقبض ملف اسمه OUTCOPY الذي هو مربوط الى نفس الجهاز المربوط فيه STDOUT كما لو كان الخرج القياسي STDOUT بنفس التاثير ، فاعادة التوجيه شيء مفيد عندما نرغب باستبدال واحد أو أكثر من مقبض للمقابض الثلاثة السابقة واعادة تخزينهم لاحقاً ،فمثلاً لإستعمال STDOUT بشكل مؤقت على الملف
#!/usr/bin/perl # file: redirect.pl print "Redirecting STDOUT\n"; open (SAVEOUT,">&STDOUT"); open (STDOUT,">test.txt") or die "Can't open test.txt: $!"; print "STDOUT is redirected\n"; system "date"; open (STDOUT,">&SAVEOUT"); #نفس ">&=SAVEOUT" print "STDOUT restored\n";
في البرنامج السابق سوف يعيد فتح STDOUT بشكل مؤقت على الملف ، ويقوم بكتابة مخرج امر النظام date على الملف من خلال الدالة system ، فعندما امر date ينفذ فسيفتح الخرج القياسي للملف ويظهر خرجة هناك بدلاًُ من نافذة الاوامر ،ولاحظ ان الدالة print ايضاً تطبع المخرج الى الملف ، ثم اعادت تخزين الـ STDOUT الى الوضع الافتراضي
وعند تشغيل السكربت فان الخرج :
Redirecting STDOUT STDOUT restored
والملف test.txt يحتوي على
STDOUT is redirected Thu Aug 10 09:19:24 EDT 2000
ويمكنك استعمال انماط اخرى للفتح الدخل القياسي (لوحة المفاتيح) والخرج القياسي (الشاشة)
open(INFO, '-'); # Open standard input open(INFO, '>-'); # Open standard output
فهو اسلوب مفيد عند اخبار البرنامج اما ان يقراء من اسم الملف المتضمن أو القراءة من الدخل القياسي
$filename = get_filename(); # subroutine, may return undef open FILE, $filename?$filename: '-'; # open standard input if no file name
اذا كانت true يقراء $filename ،واذا false يقراء STDIN
الدالة sysopen()
البيرل تؤفر ايضاً الدالة sysopen لفتح الملفات بستعمال نفس الصيغة وهي كالدالة open في مكتبة السي.
الصيغة :
$result = sysopen (FILEHANDLE,$filename,$mode [,$perms])
فهي تفتح الملف الموجود في المتغير $filename ،ثم استعمال نمط الدخل والخرج بواسطة $mode ،فاذا كان الملف غير موجود فان نمط الملف سوف ينشاءه ، ثم قيمة $perms خيارية وهي لتحديد الصلاحية او bits الترخيص للملف المنشئ ،فاذا فتح الملف بشكل صحيح فسوف يعيد true الى المتغير $result ويربط الملف المفتوح مع مقبض الملف FILEHANDLE ،واذا لم يتم فتح الملف سوف يعيد false الى المتغير $result ويترك رسائلة خطأ في المتغير الخاص $!
نمط الدالة sysopen يختلف عن نمط السابق المستعمل بواسطة الدالة open ، فبدلاً من وضع رمز نضع مجموعة من الاحرف
| النمط | الوصف |
|---|---|
| O_RDONLY | فتح الملف للقراءة فقط |
| O_WRONLY | فتح الملف للكتابة فقط |
| O_RDWR | فتح الملف لقراءة والكتابة |
| O_CREAT | إنشاء الملف إذا لم يكن موجود |
| O_EXCL | فتح ملف موجود مسبقاً |
| O_TRUNC | اذا الملف موجود مسبقاً يزيل كل بيانات الملف ليصبح حجمه صفر بايب |
| O_APPEND | فتح الملف في النمط append (مؤشر الكتابة يشير إلى نهاية الملف تكافئ >>) |
| O_NOCTTY | أذا الملف هو جهاز طرفية terminal device أفتحة بدون السماح بأن تصبح عملية الطرفية مسيطرة عليها |
| O_NONBLOCK | الملف مفتوح في النمط nonblocking |
| O_SYNC | الملف مفتوح الى نمط التزامن synchronous ، لتؤقف جميع الكتابات حتى يتم كتابة البيانات فيزئياً |
نستطيع استعمال مدى من الانماط بستعمال مؤثر " | " ، فعلى سبيل المثال لمكافئة نمط الدالة open ">" فسوف نستعمل عدة انماط من خلال نمط الدالة sysopen ، فاولاً سوف نفتح الملف للكتابة وحذف وازالة ما داخل الملف واذا الملف غير موجود سيقوم بنشاءه
use Fcntl;
sysopen(FH, "darkstar.txt",O_WRONLY|O_CREAT|O_TRUNC)
or die "Can't open: $!";موديل Fcntl القياسية تصدر هذة الثوابت المتعارفة بها وكل منها تسبق بالـ O_ ولكي نصل الى اي نمط يجب تضمين في برنامجنا use Fcntl
| sysopen Flags | fopen Mode | open Mode |
|---|---|---|
| O_RDONLY | r | < |
| O_WRONLY | O_CREAT | O_TRUNC | w | > |
| O_WRONLY | O_APPEND | O_CREAT | a | >> |
| O_RDWR | r+ | +< |
| O_RDWR |O_CREAT | O_TRUNC | w+ | +> |
| O_RDWR |O_APPEND | O_CREAT | a+ | +>> |
فعند استدعاء الدالة sysopen يجب ان يأتي واحد على الاقل من O_RDONLY, O_WRONLY, and O_RDWR، فالثوابت O_WRONLY and O_RDWR قد تستعمل مع مدى مع الانماط O_CREAT, O_EXCL, O_TRUNC, O_APPEND
O_CREAT تنشاء الملف إن لم يكن موجود ، واذا الملف غير موجود ولم يكن الثابت غير محدد عند المحاولة للكتابة فان الدالة sysopen ستفشل fail
بينما عندما تكون O_CREAT مجتمعة مع O_EXCL فهي سوف تنشئ الملف اذا لم يكن موجود واذا الملف موجود مسبقاً تفشل ويستعمل لتتجنب تضارب مع الملف الموجود
اما O_NOCTTY, O_NONBLOCK, O_SYNC ستناقش في الفصل القادمة
تراخيص الملفات في اليونكس كالـ 0644 التي تسمح لمالك الملف بالقراءة والكتابة وبقية المستخدمين بالقراءة فقط ، فاذا لم تستعمل الخيار $perm فالدالة sysopen سوف تأخذ الافتراضي وهو 0666 التي تمنح القراءة والكتابة لإي مستخدم، على اي حال سواء اذا كان الترخيص كافتراضي او بتحديد الترخيص فأن التراخيص لنشاء الملف تقدر وتحسب بواسطة bitwise AND وذلك بين ال $perm والمحتويات الحالية للمستخدم umask ، ويوضع احياناً في تقدير المستخدم لمنع الوصول الى الملف من خارج حسابة او المجموعة التي ينتمي اليها ، وفي اكثر الظروف الافضل حذف خيار التراخيص وتترك المستخدم يقوم بضبط umask لزيادة من قابلية نقل البرنامج .
لمشاهدة معلومات عن الدالة umask() بكتابة الامر perldoc -f umask
بعد فتح الملف وانشاء مقبض للملف يمكن اجراء العمليات على هذا المقبض كالقراءة او الكتابة من سطر او عدة سطور وحفظ البيانات بشكل ثنائي binary byte كملفات الصور او بشكل ملفات نصوص
فعندما نرغب القراءة من مقبض الملف يتم فتح الملف للدخل (الافتراضي )، فمثلاًُ للقراءة من مقبض الملف وتخزينها في مصفوفة حيث يقوم بقراء الملف كاملاً في ذاهب واحد
@lines =<INFO> ; # Read it into an array
في المثال السابق تم التخصيص في مصفوفة فنلاحظ ان الاختلاف في المعامل <> وهو حساس في context
$line = <FILEHANDLE> @lines = <FILEHANDLE> $line = <> @lines = <>
فنرى في التخصيص الاول تم في scalar variable لذالك بسمى بالـ scalar context وبقراءة سطر من مقبض الملف المشار ويعيد البيانات الى نهاية المقبض وبعد قراءة اخر سطر من مقبض الملف المعامل <> يعيد undef ويشير الى نهاية الملف (EOF)، وعندما المعامل مخصص كمصفوفة او يستعمل في مكان اخر بحيث ان البيرل تتوقعها بانها قائمة وتقراء كل السطور الى نهاية مقبض الملف ويعيد السطور في قائمة واحدة وهذا مايسمى بالـlist context ، وايضاً تسمى void context عندما تكون بدون تخصص الى متغير ، فالمعامل <> ينسخ السطر الى المتغير الخاص $_ عموماً void context يستعمل في التكرار while()
while (<>) {
print "Found a gnu\n" if /GNU/i;
}
ويمكن ان نستعمل foreach لطباعة المصفوفة المخزنة
foreach $LINE_VAR (@lines)
{
chop($LINE_VAR);
print $LINE_VAR;
}Chop هي لزالة السطر الجديد في النهاية ،وبستعمل الطريقة split لإنشاء العديد من المتغيرات التي نحتاج في كل مرة من خلال نفس التكرار ،وأستعمل رمز | كالفاصلة ولاحظ الرمز يحتاج الى تجاهل بالرمز \
foreach $LINE_VAR (@lines)
{
chop($LINE_VAR);
($w_name,$crowd_re,$fav_move)=split(/\|/,$LINE_VAR);
print "When $w_name is in the ring,
the crowd might $crowd_re when the $fav_move is used.\n";
}
الدالة read()
تستعمل ايضاً الدالتين read او sysread للقراءة بتحديد اكثر
الصيغة:
$bytes = read (FILEHANDLE,$buffer,$length [,$offset]) $bytes = sysread (FILEHANDLE,$buffer,$length [,$offset])
فالدالتين بنفس الصيغة وتقوم بتحديد الطول الذي نريد القراءة من مقبض الملف المشار اليه ، والمتغير $length تعني بحدود كم تريد القراءة (كم بايت من الملف ) ، حيث $buffer هو المتغير الذي سيخزن اليه مدخل البيانات
my $buffer; $bytes = read (INFO,$buffer,55);
ولاحظ ان $offset هو خياري وهو لتحديد موقع ظهور تلك البيانات المقرواءه على الموقع المحدد
فالدالتين تستعمل للقراءة البيانات الثنائي( byte-stream ) ويمكن ايضاً ان تستعمل للقراءة ملفات النصوص TEXT، لكن الاختلاف الرئيسي بينهما الدالة read تستعمل standard I/O buffering بينما الدالة sysread فلا تسعمل هذة التقنية ، فذلك يعني ان الدالة read لن تعاد حتى جلب العدد المطلوب من البيتات بالتمام او ان الملف قد وصل الى النهاية ،بينما sysread على نقيض من ذلك فهي يمكن ان تعيد قراءة جزئية ، وهي مضمون لإعادة على الاقل 1 بايت ، لكن اذا هي لاتستطيع قراءة عدد البيتات المطلوبة مباشرتاً من مقبض الملف فانها سوف تعيد مايمكن قراءتها ، سيناقش لاحقاً في Buffering and Blocking .
ملاحظة : عند قراءة البيانات كالـ byte stream مع الدالة read او sysread فان التعبير المشترك يمرر length($buffer) كالـ offset الى الـbuffer ،فالتعبير المشترك سيجعل الدالة read تذيل البيانات الجديدة الى نهاية بيانات الـ buffer :
my $buffer;
while (1) {
$bytes = read (STDIN,$buffer,50,length($buffer));
last if $bytes > 0;
}فشرط نهاية الملف EOF يحدث عندما لاتوجد اكثر من البيانات(او صفر من البيانات ) لكي تقراء من الملف او الجهاز
while (my $line = <STDIN>) {
$data .= $line;
}وعند استعمالها خارج الدالة while نستعمل الدالة defined او بإستخدام الدالة eof() التي تعيد القيمة true اذا كان على نهاية الملف EOF
$text = <>;
open MYHANDLE, "$text";
if (eof MYHANDLE) {
print "No more to read \n";
} else {
...read some more...
}فاذا إعطى مقبض الملف <> بأسماء ملفات من على نافذة الاوامر فسوف يفسر عناصر المصفوفة @ARGV كاسماء ملفات ويحاول فتحها وقراءة الملفات المخزنة
# read one or more files and print them out, prefixed by file name and line # number print "$ARGV:$.:$_" while <>;
فاسم الملف يخزن في $ARGV ، بينما المقبض ARGV ، وان كانت المصفوفة @ARGV فارغة فأن الافتراضي <> هو الدخل القياسي ويمكن تموين الدخل بواسطة $ARGV او باسم ملف ولكل قراءة يعيد المحتوى في $_ والرقم الحالي من خلال $. ،ويمكن اغلاق المقبض بواسطة close
while (<>) {
print "$ARGV: $.: $_";
close (ARGV) if eof;
}
واذا أردت كتابة شيء الى الملف فانك ستفتح الملف للخرج أو لتذيل ثم يمكنك ان استخدام print او syswrite مع بارامتر اظافي والتي تستعملن للبيانات الثناية والنصوص
فعلى سبيل المثال لكتابه اي نصوص الى الملف
print INFO "This line goes to the file.\n";
فالدالة print تستعمل لطباعة النصوص الى مقبض الملف المسمى INFO (لاحظ عمل مسافة بين المقبض والبيانات)،ولكتابة قائمة من البيانات بالفصل بينهما بعلامة , بين القائمة
$result = print FILEHANDLE $data1,$data2,$data3... $result = print $data1,$data2,$data3...
الكتابة الاولة سوف تكتب قائمة البيانات الى مقبض الملف المسمى FILEHANDLE ، بينما الكتابة الثانية سوف تكتب قائمة البيانات الى مقبض الملف الافتراضي STDOUT ويمكن تغير مقبض الملف الافتراضي بواسطة الدالة select التي لها وسيط واحد وهو المقبض المحدد ، واذا كانت الدالة print بدون بيانات فسوف تكتب محتوى المتغير الخاص $_ واذا نجحت الكتابة فالدالة سوف تعيد true واذا الكتابة فشلت تعيد false وتترك رسائلة الخطأ في المتغير الخاص $!
الدالة syswrite()
صيغة الدالة
$bytes = syswrite (FILEHANDLE,$data [,$length [,$offset]])
تستعمل ايضاً للكتابة على مقبض الملف وبإسيطرة اكبر للعملية ، فعند عدم تضمين الخيارين length و offset الدالة سوف تحاول كتابة كل البيانات الى مقبض الملف ، فالـ length لتحديد عدد البيتات التي تكتب في الموقع المعين بواسطة offset
open(INFO,">DATA.txt"); $data= "This line goes to the file.\n"; $bytes = syswrite (INFO,$data,5,3); print $bytes,"\n";
ايضاً الاختلاف الرئيسي بين الدالتين ، الدالة print يستعمل standard I/O buffering ، والدالة syswrite فلا تسعمل هذة التقنية ، syswrite ليس الدالة write فهذة الدالة هي جزء من التقارير report formatting package ولها نقاش ابعد من ذلك
الدالة select()
$previous = select(FILEHANDLE) print $previous;
الدالة select تقوم بتغير خرج مقبض الملف الافتراضي وتحدد مقبض اخر لطباعة بواسطة الدالة print ، لديها وسيط واحد وهو اسم المقبض لكي يوضع كأفتراضي ، وتعيد الاسم الافتراضي السابق الى المتغير $previous ، وهناك ايضاً select التي تأخذ اربعة بارامتر التي تستخدم للـ I/O multiplexing
هناك في البيرل بعض التعابير التي تسمح لفحص الملف والتالي بعض منها فمثلاًُ لروية هل موجود الملف
$neededfile="myfile.pl";
if (-e $neededfile)
{
#proceed with your code
}–e مع IF تختبر هل هو موجود ، وفي داخل البلوك تجرى العمليات ،وإن كان الملف غير موجود نعمل رسائله خطأ مثلاً
ويمكن فحص الملف اذا موجود واذا كان حجمه يساوي صفر( خالي من البيانات) نستعمل -z
ويمكن فحص الملف اذا موجود واذا كان حجمه لا يساوي صفر نستعمل -s
ويمكن ايضاً أن الملف يسمح بالقراءة او الكتابة او التنفيذ بستخدام :
تمكن القراءة : -r
تمكن الكتابة : -w
تمكن التنفيذ : -x
$readfile="myfile";
if ( (-e $readfile) && (-r $readfile) )
{
#proceed with your code
}ويمكن الفحص اذا كان الملف text او binary
ملف Text -T
ملف Binary -B
#!/usr/bin/perl -w
$siteurl="http://www.sniper-sa.com";
$description="A Super Duper Site";
$file_name="data.txt";
# write to the data file
open(DAT,">>$file_name") || die("Cannot Open File");
print DAT "$siteurl\|$description\n";
close(DAT);
#executes the file
system "$file_name";
#waits
sleep(3);
#create the file
$lockfile="lock_the_file.loc";
open (LOCK,">$lockfile") || die ("Cannot open lock file!\n");
close (LOCK);
#Deleting the file
unlink($lockfile);
$readfile="myfile";
if ( (-e $readfile) && (-r $readfile) )
{
open(INF,">>$readfile") || die("Cannot Open File");
}تؤفر البيرل اجراءات خاصة مع مقبض ملف ،مقبض الملف الخاص DATA سوف تقراء البيانات المدخلة من ملف البرنامج والمتضمنة مع التوكن__DATA__ في البرنامج ، فكل سطر في هذة الـtoken سوف يترجم او يجمع بواسطة مفسر البيرل ويكون متؤفراً كمدخل للمقبض الملف DATA الى نهاية الملف او الى التوكن __END__ أن كان هو الحالي
في بعض الاوقات برنامج البيرل ينظر الى إستعمال __END__ لنفس هذا الغرض ، ويبدو هذا مشوش لكون __END__ غرضها لإيقاف ترجمة البيرل ، وهذا بسبب ان النسخ التي قبل البيرل 5 كأن __END__ دورة كما تعمل __DATA__ في الوقت الحالي مع الكود الرئيسي لكنة لايتأثر على الحزم الاخرى اوملف متضمن من خلال التوجيه do او require
البيانات المعرفة ضمن البرنامج يمكن ان تكون سهلة جداًُ ، فعلى سبيل المثال يمكننا تعريف ملف للإعداءات بدون انشاء او تحميل البيانات من ملف مستقل وايضاً هو سهل لتعريف تركيب بيانات كبيرة في شكل معقدة او ان البيانات لاترغب في تغيرها ، ففي المثال يقوم بتخزين مجموعة من البيانات في الجدول المسمى %table ويقراء البيانات من مقبض الملف الخاص DATA :
#!/usr/bin/perl
# planets.pl
use warnings;
use strict;
my $columns = <DATA>;
chomp $columns;
my @columns = split /\s*, \s*/, $columns;
shift @columns; # lose first name
my %table;
while (<DATA>) {
next if /^#/; # skip comments
my @data = split /\s*, \s*/;
my $name = shift @data;
foreach (0..$#data) {
print "$_ : $columns[$_] : $data[$_] \n";
$table{$name}{$columns[$_]} = $data[$_];
}
}
foreach (sort keys %table) {
print "$_\n";
foreach my $stat (sort keys %{$table{$_}}) {
print "\t$stat = $table{$_}{$stat}\n";
}
}
__DATA__
Body , Radius , Mass , Distance, Moons, Day , Year
# The Sun
Sun , 6.960e8, 1.989e30, 0 , n/a , 25.36 days , n/a
# The Planets
Mercury , 2.420e6, 3.301e23, 5.791e10, 0 , 58.7 days , 87.97 days
Venus , 6.085e6, 4.869e24, 1.082e11, 0 , 243 days , 224.7 days
Earth , 6.378e6, 5.978e24, 1.496e11, 1 , 23.93 hours, 365.3 days
Mars , 3.375e6, 6.420e23, 2.279e11, 2 , 24.6 hours , 687 days
Jupiter , 7.140e7, 1.899e27, 7.783e11, 13 , 9.9 hours , 11.86 years
Saturn , 6.040e7, 5.685e26, 1.427e12, 10 , 10.2 hours , 28.46 years
Uranus , 2.360e7, 8.686e25, 2.869e12, 5 , 10.7 hours , 84.02 years
Neptune , 2.230e7, 1.025e26, 4.498e12, 2 , 15.8 hours , 164.8 years
Pluto , 3.000e6, 5.000e23, 5.900e12, 1 , 6.3 days , 248 years
# And the Moon, just for luck
Moon , 1.738e6, 7.353e22, 1.496e11, n/a , 27.32 days , 27.32 days
__END__
A comment - this is neither part of the program nor the data (but see below...)
فإذا ظهر التوكن __END__ بعد التوكن __DATA__ فأن نهاية البيانات تنتهي حالما تصادف __END__ وماعدا ذلك فسوف يترجم الى نهاية الملف ، الحقيقة ان التوكن __DATA__ في مجال رؤية الحزمة package scope ويمكن الوصول للبيانات من مختلف الحزم بتقديم مقبض الملف مع اسم الحزمة بهذا الشكل :
# access data in MyPackage
while (<MyPackage::DATA>) {
...
}
كما هو مذكور في وقت سابق تختلف معالج __END__ لإسباب توافقية وذلك عند إستعمال __END__ في الحزمة الرئسية فهي تعمل كما لو أضيفت __DATA__ مرة اخرى ، على اي حال فأن البيانات التي بعد التوكن __END__ غير معالجة في البرنامج السابق ، وتتعامل مع هذة التوكن بشئ ما كما لو كانت حزمة مع حزمة اخرى حيث يظهر التشوش في قراءة ما بعد __END__ فهي لاتحتوي على اي فاصلة وعند اظافة فاصلة بدلاُ من الرمز - او اضافة سطر من البيانات تسبب في ظهوره في الخرج التالي عند تشغيل البرنامج .
اضيفت هذة الميزة للبيرل 5.8 فما فوق للمعالجة المتغيرات كما لو كانت ملفات في الذاكرة ، ونستطيع فتح متغير بدلا من اسم الملف للقراءة او للتعديل او الكتابة او عمل seek لمختلف المواقع في الذاكرة .... وفي نفس الوقت يمكننا الوصول الى المقبض فعلى سبيل المثال
#!/usr/bin/perl # inmemoryfiles.pl use strict; use warnings; my $text="The Giant's Game\nThe End Of The World\n"; open MEMFILE, "<", \$text; print scalar(<MEMFILE>); # produces "The Giant's Game" close MEMFILE; my $log=""; open LOGFILE, ">", \$log; print LOGFILE "The Enemy's Gate"; print LOGFILE " is down\n"; close LOGFILE; print $log; # produces "The Enemy's Gate Is Down"
ولاحظ أستخدام الدالة scalar يمكن أزالتها بدون حدوث أي تاثير سواء تعيد سطر واحد ويمكن أن تضاف الى المقبض لتحفظ الى الملف
فمقبض الملف حالة خاصة يمكنك امرارة بواسطة typeglob
# sub say_hello {
*WHERE = shift;
print WHERE "Hi there!\n"
}
say_hello(*STDOUT);
او بإستعمال مرجع الى typeglob ،فالمرجع اما يكون مرجع فعلي او مرجع رمزي ,وكما سبق لمعالجة typeglobs في سياق scalar يولد مرجع رمزي الى الـtypeglob
sub say_hello {
my $fh = shift; #$fh becomes '*main::STDOUT'
print $fh "Hi there!\n"
}
sub get_line {
my $fh = shift;
my $response = <$fh>;
chomp $response;
$response =~ s/^\s+//;
return $response;
}
say_hello(*STDOUT);
get_line (*STDIN );
فبدلاً من ذلك يمكن انشاء مرجع الى الـtypeglob بإستخدام غير مباشر للإشارة الى مقبض الملف
say_hello(\*STDOUT); # reference to typeglob get_line (\*STDIN );
امثلة اخرى للملفات:
لنسخ ملف الى ملف اخر ، فاولاً فتح الملف المصدر للقراءة و فتح الملف الهدف للكتابة ثم القراءة من الملف المصدر والكتابة الى الهدف ثم اغلاق الملفات
#- Copy.pl
# usage:perl copy.pl scourefile.txt destinationfile.txt
($in, $out) = @ARGV;
die "Missing input file name.\n" unless $in;
die "Missing output file name.\n" unless $out;
$byteCount = 0;
open(IN, "< $in");
binmode(IN);
open(OUT, "> $out");
binmode(OUT);
while (read(IN,$b,1))
{
$byteCount++;
print(OUT $b);
}
close(IN);
close(OUT);
print "Number of bytes copied = $byteCount\n";
exit;الدالة binmode() لوضع مقبض الملف الى نمط الثنائي في الذاكرة بدلاًُ من التخزن في النمط النصي
تحويل البيانات الى الترميز السادس عشر :
#!/usr/bin/perl
##- Bin2Hex.pl
($in, $out) = @ARGV;
die "Missing input file name.\n" unless $in;
die "Missing output file name.\n" unless $out;
$byteCount = 0;
open(IN, "< $in");
binmode(IN);
open(OUT, "> $out");
while (read(IN,$b,1))
{
$n = length($b);
$byteCount += $n;
$s = 2*$n;
print (OUT unpack("H$s", $b), "\n");
}
close(IN);
close(OUT);
print "Number of bytes converted = $byteCount\n";
exit;و لعودة البيانات من السادس عشر:
#!/usr/bin/perl
##- Hex2Bin.pl
($in, $out) = @ARGV;
die "Missing input file name.\n" unless $in;
die "Missing output file name.\n" unless $out;
$byteCount = 0;
open(IN, "< $in");
open(OUT, "> $out");
binmode(OUT);
while (<IN>)
{
chop;
$s = length($_);
$byteCount += $s/2;
print (OUT pack("H$s", $_));
}
close(IN);
close(OUT);
print "Number of bytes converted = $byteCount\n";
exit;
عند انجاز عمليات خرج أو دخل لسطر موجهه قد تشاهد اختلاف الحرف الاخير في نهاية السطر بأختلاف نظام التشغيل لديك ، في اليونكس نهاية السطر مع السطر الجديد هو ( LF, octal \012 في ASCII Table ) وعلى خلاف الونيدوز مصمم في كل نهاية سطر نصي لديه حرفين وهما السطر الجديد والإعادة ( CRLF, or octal \015\012 ) و يظهر هذا الاختلاف مع سيرفرات الشبكة التي تستعمل غالباً CRLF في نهاية السطر ، و هذا يؤدي الى بعض التشويش عند نقل الملفات النصية بين الاجهزة على الشبكة .
يؤفر مفسر البيرل طريقة لفحص ولتغير الحرف الاخير في نهاية السطر من خلال المتغير العام $/ ويحتوي هذا المتغير على الحرف الحالي(او قد يكون سلسة من الاحرف) يستخدم للإشارة الى نهاية السطر والافتراضي له يوضع بالقيمة \012 في اليونكس و \015\012 على الونيدوز ، فعلى سبيل المثال السطر الموجه للوظيفة الدخل <> تقراء من المقبض المحدد الى ان يصادفة الحرف(او الاحرف ) الاخير في محتوي المتغير الخاص $/ ويرجع السطر النصي مع الحرف الاخير في مجموعة مرتبطة ، و دالة chomp تبحث عن الحرف الاخير في نهاية النص وازالتة والتي هي متعلقة بالقيمة الحالية للمتغير الخاص $/ .
الحرف الخاص \n هو حرف السطر الجديد Linefeed كما أسلفنا يختلف على انظمة التشغيل فهو يكافي \012 على اليونكس وفي التالي 012 التي يقابلها في العشري 10 ، وعادة في الونيدوز \012\015 (في العشري 12 +10 ) مجتمعاً مع الحرف \r حرف اعادة الحمل carriage return ، وعلى نظام الماكنتوش العكس
print "This is a new line in octal \012\015";
فعندما نتصل بالسيرفرات الشبكة مع السطر الموجه التي تستعمل CRLF في نهاية السطر فلن يكون البرنامج المحلي portable اي لايكون قابل للنقل الى انظمة اخرى عند وضع المتغير الخاص $/ بالقيمة \r\n اوبدقة عند عبور \015\012 ، ولجعل هذا اسهل فأن Socket وموديل IO::Socket يمكن ان تصدير العديد من الثوابت والمتغيرات $CRLF, $CR, and $LF وذلك لإعادة القيم الصحيحة والتالي قائمة من بعض الثوابت المصدر بواسطة الوحـدة Socket
| الاسم | الوصف |
|---|---|
| CRLF | الثابت الذي يحتوي على الاحرف CRLF |
| CR | الثابت الذي يحتوي على الحرف CR |
| LF | الثابت الذي يحتوي على الحرف LF |
لايصدر في الافتراضي الثوابت $CRLF, $CR, and $LF عند استخدام الوحدة ،ولتصديرها يجب ان تجلب بإستيردها بواسطة التاج ":crlf" ويجب ان تحتاج لإستيراد باقي الثوابت الافتراضية المتعلقة بالوحدة socket بواسطة التاج ":DEFAULT" لإستخدام في البرنامج وهذا من خلال
use Socket qw(:DEFAULT :crlf);
هناك تعقيد اضافي فعند انجاز line-oriented I/O على بيئة الونيدوز حيث يميز بين مقابض الملفات اذا كانت على النمط النصي او الثنائي ، ففي النمط الثنائي عند طباعة مقبض الملف الثنائي فالبيانات تنتج كما حددت بالضبط ، وهي مماثل لعملية القراءة التي تعيد البيانات المخزنة في الملف بالتمام .
اما في النمط النصي فأن مكتبة standard I/O يترجم بشكل آلي LF الى الزوج CRLF في طريقة الـout ، ويترجم الزوج CRLF الى LF في طريقة الـin ، وهذة الميزة في البيرل لجعل العملية في النمط النصي على اليونكس والونيدوز بنفس الشكل . في قراءة او كتابة ملفات ثنائية ( كصور ) تنج بشكل غامض في الخرج وهذا بسبب ان نهاية السطر الافتراضي يترجم لذا يجب علينا ايقاف الحرف المترجم بإستداعى الدالة binmode() على مقبض الملف .
binmode (FILEHANDLE [$discipline])
تعيد هذة الدالة مقبض الملف الى النمط الثنائي وعدم ترجمة الحرف المترجم ، والوسيط الاول هو مقبض الملف ، مع أستخدام الوسيط الثاني الخياري فسوف يعاد الى النمط الثنائي مع انجاز القيمة :raw علية ، او باعادة تخزين النمط النصي الافتراضي بإستخدام القيمة :crlf ، و فقط الدالة لها تاثير على انظمة الونيدوز و VMS لكون الحرف الاخير هو اكثر من حرف ، اما على اليونكس بدون تأثير.
الطريقة الاخرى للتجنب هذا التشويش في النمط الثنائي او النصي عن طريق أستخدام الدالتين sysread و syswrite لتخطي روتينات الحرف المترجم في المكتبة standard I/O والتي تسيطر على المتغيرات العامة لسطر الموجهه باتذيل حرف نهاية السطر EOl بشكل آلياً الى البيانات المطبوعة
على سبيل المثال الطباعة بواسطة print
#!/usr/bin/perl
$_ = "Starting:\n";
print;
print("1+1=",1+1,"\n");
print(STDOUT "(1+2)*3=",(1+2)*3,"\n");
$v = "OUT";
open($v,"> out.tmp");
print($v); #prints the value in $v to STDOUT.
الى الشاشة # $vطباعة قيمة المتغير
print(OUT); # طباعة قيمة المتغير الخاص الى مقبض الملف OUT
print($v "The end.\n"); #الطباعة باتحديد
exit;لاحظ ذلك \n تطبع \r\n على الونيدوز وحجم الملف out.tmp ستراه 21 بايت ، لكن قد قمنا بعمل 19 بايب في الحقيقة . يمكن التحقق من ذلك بفتح الملف بمحرر السداس عشر حيث \n (0x0A) و \r (0x0D) لذا تستخدم الدالة chop لزالة الحرف.
#!/usr/bin/perl
#- InputStat.pl
($file) = @ARGV;
die "Missing file name.\n" unless $file;
$recordCount = 0;
$charCount = 0;
$totalCount = 0;
open(IN, "< $file");
while (<IN>)
{
$recordCount++;
$totalCount += length($_);
chop;
$charCount += length($_);
}
close(IN);
print "Number of records = $recordCount\n";
print "Number of characters after chop = $charCount\n";
print "Number of total characters = $totalCount\n";
exit;على الونيدوز: نفرض ان لدينا الملف النصي "123\r\nABC\r\n" الذي هو 10 بايت ، وله 2 record . و \r\n على نهاية كل record سيكون الخرج :
Number of records = 2 Number of characters after chop = 6 Number of total characters = 8
على اليونكس : نفرض ان لدينا ملف اخر "1234\nABCD\n" له 10 بايت ، وله 2 records و \n على نهاية كل record
Number of records = 2 Number of characters after chop = 8 Number of total characters = 10
نفرض ان لدينا ملف ثالث "1234\rABCD\r" له 10 بايت ،و له 2 records و \r على نهاية كل record
Number of records = 1 Number of characters after chop = 9 Number of total characters = 10
تشترك I/O errors في تطبيقات الشبكة وجميع التطبيقات التي تتعامل مع الخرج والدخل وكقاعدة فكل دوال البيرل التي تنجز عمليات الخرج والدخل تعيد undef ، قيمة false في حالتة الفشل failure ، وتعين تلك المعلومات بفحص المتغير الخاص $! ، ولهذا المتغير شيء يجلب الانتباه الذي يعبر كنصوص في رسالة الخطأ مثل Permission denied ، وفي تعاملها كعدد حيث تعيد ثابت عددي لكل خطأ كما هو معرف من قبل نظام التشغيل فمثلاً EACCES له رقم الخطأ الخاص بة بالمقابل فأن لكل ثابت عددي رقم خاص ، المهم ان إستخدام هذة الثوابت العددية موثوق أكثر لتمييز الأخطاء عن بعضها كونها المعيار الاوحد في نظام التشغيل .
نستطيع الحصول على قيم ثوابت رسائل الخطأ بإستيرادهم من Errno module ، ونستطيع إستيرادها بواسطة use اما فردية بواسطة الإسم او جميعهم معاً ، لجلب الثوابت فردية بالإسم نضع في التوجيه ما نرغب في إستيرادة كما هو مشاهد هنا :
use Errno qw(EACCES ENOENT);
my $result = open (FH,">/etc/passwd");
if (!$result) { # oops, something went wrong
if ($! == EACCESS) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File or directory not found.";
} else {
warn "Some other error occurred: $!";
}
}
كما سبق أستخدام المعامل qw() لتقسيم النصوص الى قائمة من الكلمات عند كل فراغ وهي مكافئة :
use Errno ('EACCESS','ENOENT');لجلب ثوابت EACCESS و ENOENT ومن الملاحظ في المثال السابق عند مقارنة $! مع هذة الثوابت نستعمل معامل المقارنة " == "
لجلب جميع ثوابت الخطأ المشتركة نستورد التاج :POSIX ، هذا يجلب ثوابت الخطأ التي معرفة من قبل معيار POSIX القياسي عبر الارصفة API المختلفة منها يونكس ونيدوز والعديد من الإنظمة الاخرى الخاضعة لهذا المعيار ، فمثلاً :
use Errno qw(:POSIX);
تتقلب إتصالات الإنترنت في الشبكة وسنواجه لاحقاً الاخطاء والاستثناءات مع برمجة الشبكات سنذكر مقدمة و